#include "displayable.h"

/**
 * 1.4.12 - Steven Hartin - Added bounding data into the displayable object.
 *
 * 17.3.12 - Steven Hartin - Added the ability to not render subsets. Allows
 *  us to perform simple occlusion.
 *
 * 16.3.12 - Steven Hartin - Added the ability to display animation hierarchies
 */

CDisplayable::CDisplayable():
    m_pMesh(nullptr), 
    m_fRotateX(0.0f),
    m_fRotateY(0.0f),
    m_fRotateZ(0.0f),
    m_fTranslateX(0.0f),
    m_fTranslateY(0.0f),
    m_fTranslateZ(0.0f),
    m_fScaleX(1.0f),
    m_fScaleY(1.0f),
    m_fScaleZ(1.0f),
    m_fVelocityX(0.0f), 
    m_fVelocityY(0.0f), 
    m_fVelocityZ(0.0f),
    m_nAnimationID(-1),
    m_nCurrentAnimationSet(0),
    m_bShowBoundingBox(false),
    m_bResetAnimationFrameTime(false),
    m_bAutomaticallyRendered(true)
{
    /**
     * Constructor. sets all variables to to 0 and pointers to nullptrs
     */
    m_BoundingShape.fRadius = 0.0f;
    m_BoundingShape.vecSphereCentre = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
    m_BoundingShape.vecMinBound = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
    m_BoundingShape.vecMaxBound = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
}

CDisplayable::CDisplayable(CMesh *pMesh):
    m_pMesh(pMesh), 
    m_fRotateX(0.0f),
    m_fRotateY(0.0f),
    m_fRotateZ(0.0f),
    m_fTranslateX(0.0f),
    m_fTranslateY(0.0f),
    m_fTranslateZ(0.0f),
    m_fScaleX(1.0f),
    m_fScaleY(1.0f),
    m_fScaleZ(1.0f),
    m_fVelocityX(0.0f), 
    m_fVelocityY(0.0f), 
    m_fVelocityZ(0.0f),
    m_nAnimationID(-1),
    m_nCurrentAnimationSet(0),
    m_bShowBoundingBox(false),
    m_bResetAnimationFrameTime(false),
    m_bAutomaticallyRendered(true)
{
    /**
     * Constructor. Sets all variables to to 0 and pointers to nullptrs. Assigns
     * a mesh directly to the displayable object. Assumes the mesh has already
     * been loaded / initialised from a file.
     *
     * @param CMesh *pMesh A mesh object to be held by the displayable.
     */
    m_BoundingShape = pMesh->getBoundingShape();
}

CDisplayable::CDisplayable(const string &szKey):
    m_pMesh(nullptr), 
    m_fRotateX(0.0f),
    m_fRotateY(0.0f),
    m_fRotateZ(0.0f),
    m_fTranslateX(0.0f),
    m_fTranslateY(0.0f),
    m_fTranslateZ(0.0f),
    m_fScaleX(1.0f),
    m_fScaleY(1.0f),
    m_fScaleZ(1.0f),
    m_fVelocityX(0.0f), 
    m_fVelocityY(0.0f), 
    m_fVelocityZ(0.0f),
    m_nAnimationID(-1),
    m_nCurrentAnimationSet(0),
    m_bShowBoundingBox(false),
    m_bResetAnimationFrameTime(false),
    m_bAutomaticallyRendered(true)
{
    /**
     * Constructor. sets all variables to to 0 and pointers to nullptrs.
     * Attempts to load a mesh from a given key name. Assumes the mesh is
     * already in the mesh container indexed by the string key name.
     * If it isn't in, this will throw an exception.
     *
     * @param const string &szKey The key of the mesh to use.
     * @throw runtime_error If a mesh identified by the key cannot be found.
     */
    CMeshContainer *pMeshes(CMeshContainer::getInstance());
    if ((m_pMesh = pMeshes->getMesh(szKey)) == nullptr)
    {
        stringstream sstream;
        sstream << "CDisplayable::CDisplayable(const string&)"
                << " key '"
                << szKey
                << "' is not a valid mesh key";
        throw std::runtime_error(sstream.str());
    }    

    m_BoundingShape = m_pMesh->getBoundingShape();
}

void CDisplayable::loadAnimation(const string &szFileName)
{
    /**
     * Loads an X file with all the frame hierarchy still in tact, which
     * gives us the ability to render 
     *
     * @param const string &szFileName The name of the file to load 
     */
    IXAnimator *pAnimations(
            CDisplayableContainer::getInstance()->getAnimatorObject());
    if (! pAnimations->LoadXFile(
            szFileName,
            &m_nAnimationID,
            XANIMATOR_CLEAN_MESH |
            XANIMATOR_OPTIMISE_MESH))
    {
        throw std::runtime_error((string("Cannot load file: ") + 
                                  szFileName).c_str());
    }
    
    m_nCurrentAnimationSet = 0;
    pAnimations->ChangeAnimationSet(m_nAnimationID,
                                    m_nCurrentAnimationSet,
                                    250.0f);
    m_LastFrameTime.setTime();

    pAnimations->GetBoundingShapes(m_nAnimationID, &m_BoundingShape.vecMinBound, 
                                   &m_BoundingShape.vecMaxBound,
                                   &m_BoundingShape.vecSphereCentre, 
                                   &m_BoundingShape.fRadius, 0);
}

void CDisplayable::setAnimationSet(int nCurrentAnimationSet)
{
    /**
     * Sets the animation set to start playing another set from the animation
     * hierarchy.
     *
     * @param int nCurrentAnimationSet The animation set to change to.
     */
    if (nCurrentAnimationSet != m_nCurrentAnimationSet)
    {
        m_nCurrentAnimationSet = nCurrentAnimationSet;
        IXAnimator *pAnimations(
                CDisplayableContainer::getInstance()->getAnimatorObject());
        pAnimations->ChangeAnimationSet(m_nAnimationID, m_nCurrentAnimationSet, 
            250.0f);
        m_LastFrameTime.setTime();
        m_bResetAnimationFrameTime = true;
    }
}


int CDisplayable::getCurrentAnimationSet() const
{
    /**
     * Sets the animation set to start playing another set from the animation
     * hierarchy.
     *
     * @param int nCurrentAnimationSet The animation set to change to.
     */
    return m_nCurrentAnimationSet;
}

void CDisplayable::setMaterial(CMaterial Material, DWORD dwSubset)
{
    /** 
     * Adds a material to material array and set its subset. 
     *
     * @param CMaterial pMaterial The material to add to the material list 
     * @param DWORD dwSubset The subset to apply the material to. 
     */

    map<DWORD, CMaterial>::iterator pMaterialPos(
        m_arrMeshMaterials.find(dwSubset));
    if (pMaterialPos != m_arrMeshMaterials.end())
    {
        m_arrMeshMaterials.erase(pMaterialPos);
    }
    m_arrMeshMaterials.insert(pair<DWORD,CMaterial>(dwSubset, Material));
}

void CDisplayable::setTexture(CTexture* texture, DWORD dwSubset){
    /** 
     * Adds a texture to texture array and set its subset.
     *
     * @param CTexture* texture The texture to add to the texture array.
     * @param DWORD dwSubset The subset to apply the texture to. 
     */
    map<DWORD, CTexture*>::iterator pTexture(m_arrMeshTextures.find(dwSubset));
    if (pTexture != m_arrMeshTextures.end()) 
    {
        m_arrMeshTextures.erase(pTexture);
    }
    m_arrMeshTextures.insert(pair<DWORD,CTexture*>(dwSubset, texture));
}

CDisplayable::~CDisplayable()
{
    /**
     * Destructor, ensure the memory for all the textures are released.
     */
    m_pMesh = nullptr;
}

void CDisplayable::assignMesh(CMesh *pMesh)
{
    /**
     * Assigns a mesh to be used by this displayable object.
     *
     * @param CMesh *pMesh The mesh to use by this dispalayable object.
     */
    m_pMesh = pMesh;
}

void CDisplayable::render()
{
    /**
     * Loads the transformations, textures and materials needed, then renders
     * each subset from the applied mesh.
     */
    D3DXMATRIX matWorld, matScale, matTranslate, matRotate;

    // Set WorldMat to identity matrix
    D3DXMatrixIdentity(&matWorld);                                
    
    D3DXMatrixScaling(&matScale, m_fScaleX, m_fScaleY, m_fScaleZ);

    D3DXMatrixTranslation(&matTranslate, m_fTranslateX, m_fTranslateY, 
                          m_fTranslateZ);
    
    D3DXMatrixRotationYawPitchRoll(&matRotate, m_fRotateY, 
                                   m_fRotateX, m_fRotateZ);
    
    // First scale, then rotate, and finally translate
    D3DXMatrixMultiply(&matWorld, &matWorld, &matScale);
    D3DXMatrixMultiply(&matWorld, &matWorld, &matRotate);
    D3DXMatrixMultiply(&matWorld, &matWorld, &matTranslate);

    // Apply the transformations to the 3D world
    CDeviceManager::getInstance()->getDevice()->SetTransform(D3DTS_WORLD, 
                                                             &matWorld);
    
   if (m_nAnimationID == -1)
    {
        // Activate material and texture for that section.
        if (m_pMesh != nullptr)
        {
            // Reload the original bounding shape
            m_BoundingShape = m_pMesh->getBoundingShape();
            _updateBoundingData(matWorld);

            if ( isVisible())
            {
                // Draw the mesh in sections according to the different textures 
                // and materials.
                for (DWORD nSubset(0); nSubset < m_pMesh->getSubsetCount(); 
                     ++nSubset)
                {
                    if (! isSubsetDrawable(nSubset))
                    {
                        // This subset is not drawable go to next subset.
                        continue;
                    }

                    // If displayable has a material, display it, otherwise, if 
                    // the mesh has a material, apply the default one instead.
                    if (hasMaterial(nSubset))
                    {
                        getMaterial(nSubset).applyMaterial(
                              CDeviceManager::getInstance()->getDevice());
                    }
                    else if (m_pMesh->hasMaterial(nSubset))
                    {
                        m_pMesh->getMaterial(nSubset).applyMaterial(
                                    CDeviceManager::getInstance()->getDevice());
                    }

                    // If we have a texture, display it, otherwise, if the mesh
                    //  has a texture, apply the default one instead.
                    if (hasTexture(nSubset))
                    {
                        getTexture(nSubset)->applyTexture(0, 
                                    CDeviceManager::getInstance()->getDevice());
                    }
                    else if (m_pMesh->hasTexture(nSubset))
                    {
                        m_pMesh->getTexture(nSubset)->applyTexture(0, 
                                   CDeviceManager::getInstance()->getDevice());
                    }
            

                    // Render the subset
                    m_pMesh->render(nSubset, 
                                    CDeviceManager::getInstance()->getDevice());
                }
            }
        }
    }
    else
    {
        // We need to animate the frame.
        IXAnimator *pAnimations(
            CDisplayableContainer::getInstance()->getAnimatorObject());

        // Reload the original bounding shape
        int m_nBounds(0);
        TMeshBounds *pBounds(pAnimations->GetBoundingShapes(
                                       m_nAnimationID, 
                                       &m_BoundingShape.vecMinBound, 
                                       &m_BoundingShape.vecMaxBound,
                                       &m_BoundingShape.vecSphereCentre, 
                                       &m_BoundingShape.fRadius, 
                                       &m_nBounds));
        _updateBoundingData(matWorld);

        if (isVisible())
        {
            pAnimations->Render(m_nAnimationID, matWorld,
                      static_cast<float>(m_LastFrameTime.getTimeDifference()));
            m_LastFrameTime.setTime();
        }
    }

    // Comment out the "return" command to draw the bounding data
    
    if (!(CDeviceManager::getInstance()->getCheatMode() 
          & Constants::BOUNDING_BOXES ||
        (CDeviceManager::getInstance()->getCheatMode() 
          & Constants::OCCUPIED_BOUNDING_BOXES
         && m_bShowBoundingBox)))
    {
        return;
    }

    float fMinX(m_BoundingShape.vecMinBound.x);
    float fMinY(m_BoundingShape.vecMinBound.y);
    float fMinZ(m_BoundingShape.vecMinBound.z);

    float fMaxX(m_BoundingShape.vecMaxBound.x);
    float fMaxY(m_BoundingShape.vecMaxBound.y);
    float fMaxZ(m_BoundingShape.vecMaxBound.z);

    D3DXVECTOR3 p0(fMinX, fMinY, fMinZ);
    D3DXVECTOR3 p1(fMaxX, fMinY, fMinZ);
    D3DXVECTOR3 p2(fMaxX, fMaxY, fMinZ);
    D3DXVECTOR3 p3(fMinX, fMaxY, fMinZ);

    D3DXVECTOR3 p4(fMinX, fMinY, fMaxZ);
    D3DXVECTOR3 p5(fMaxX, fMinY, fMaxZ);
    D3DXVECTOR3 p6(fMaxX, fMaxY, fMaxZ);
    D3DXVECTOR3 p7(fMinX, fMaxY, fMaxZ);
    
    D3DXMATRIX matViewProjection(CCamera::getInstance()->getViewMatrix() * 
                                 CCamera::getInstance()->getProjectionMatrix());

    LPD3DXLINE line;
    D3DXCreateLine(CDeviceManager::getInstance()->getDevice(), &line);

    D3DXVECTOR3 lines[16] = {p0, p1, p2, p3, p0, 
                             p4, p7, p3, p2, p6,
                             p7, p4, p5, p6, p5,
                             p1};
    line->Begin();
    line->DrawTransform(lines, 16, &matViewProjection, 0xFFFFFFFF);
    line->End();
    line->Release();
}

void CDisplayable::_updateBoundingData(const D3DXMATRIX &matWorld)
{
    /**
     * Updates the bounding box information with the world transformation.
     *
     * @param const D3DXMATRIX &matWorld The world transformation
     */
    D3DXVec3TransformCoord(&m_BoundingShape.vecMinBound, 
                           &m_BoundingShape.vecMinBound,
                           &matWorld);
    D3DXVec3TransformCoord(&m_BoundingShape.vecMaxBound, 
                           &m_BoundingShape.vecMaxBound,
                           &matWorld);
    D3DXVec3TransformCoord(&m_BoundingShape.vecSphereCentre, 
                           &m_BoundingShape.vecSphereCentre,
                           &matWorld);
}

void CDisplayable::setXTranslate(float fX)
{
    /**
     * @param float x The x value of the transformation
     */
    m_fTranslateX = fX;
}

void CDisplayable::setYTranslate(float fY)
{
    /**
     * @param float y The y value of the transformation
     */
    m_fTranslateY = fY;
}

void CDisplayable::setZTranslate(float fZ)
{
    /**
     * @param float z The z value of the transformation
     */
    m_fTranslateZ = fZ;
}

float CDisplayable::getTranslateX() const
{
    /**
     * @return The x value of the transformation
     */
    return m_fTranslateX;
}

float CDisplayable::getTranslateY() const
{
    /**
     * @return The y value of the transformation
     */
    return m_fTranslateY;
}

float CDisplayable::getTranslateZ() const
{
    /**
     * @return The z value of the transformation
     */
    return m_fTranslateZ;
}

void CDisplayable::getTranslate(float &fX, float &fY, float &fZ)
{
    /**
     * @param float &x The x value of the transformation
     * @param float &y The y value of the transformation
     * @param float &z The z value of the transformation
     */
    fX = m_fTranslateX;
    fY = m_fTranslateY;
    fZ = m_fTranslateZ;
}

void CDisplayable::setTranslate(float fX, float fY, float fZ)
{
    /**
     * Translates this given object by a specified distance.
     *
     * @param float x The amount to translate in the X axis.
     * @param float y The amount to translate in the Y axis.
     * @param float z The amount to translate in the Z axis.
     */
    m_fTranslateX = fX;
    m_fTranslateY = fY;
    m_fTranslateZ = fZ;
}

void CDisplayable::setXRotate(float fX)
{
    /**
     * @param float x The x value of the transformation
     */
    m_fRotateX = fX / 180.0f * D3DX_PI;
}

void CDisplayable::setYRotate(float fY)
{
    /**
     * @param float y The y value of the transformation
     */
    m_fRotateY = fY / 180.0f * D3DX_PI;
}

void CDisplayable::setZRotate(float fZ)
{
    /**
     * @param float z The z value of the transformation
     */
    m_fRotateZ = fZ / 180.0f * D3DX_PI;
}

float CDisplayable::getRotateX() const
{
    /**
     * @return The x value of the transformation
     */
    return m_fRotateX  * 180.0f / D3DX_PI;
}

float CDisplayable::getRotateY() const
{
    /**
     * @return The y value of the transformation
     */
    return m_fRotateY  * 180.0f / D3DX_PI;
}

float CDisplayable::getRotateZ() const
{
    /**
     * @return The z value of the transformation
     */
    return m_fRotateZ  * 180.0f / D3DX_PI;
}

void CDisplayable::getRotate(float &fX, float &fY, float &fZ)
{
    /**
     * @param float &x The x value of the transformation
     * @param float &y The y value of the transformation
     * @param float &z The z value of the transformation
     */
    fX = m_fRotateX * 180.0f / D3DX_PI;
    fY = m_fRotateY * 180.0f / D3DX_PI;
    fZ = m_fRotateZ * 180.0f / D3DX_PI;
}

void CDisplayable::setRotate(float fX, float fY, float fZ)
{
    /**
     * Rotates this given object by a specified value theta in degrees.
     *
     * @param float x The amount to rotate around the X axis in degrees.
     * @param float y The amount to rotate around the Y axis in degrees.
     * @param float z The amount to rotate around the Z axis in degrees.
     */
    m_fRotateX = (fX / 180.0f) * D3DX_PI; 
    m_fRotateY = (fY / 180.0f) * D3DX_PI; 
    m_fRotateZ = (fZ / 180.0f) * D3DX_PI; 
}

void CDisplayable::setXScale(float fX)
{
    /**
     * @param float x The x value of the transformation
     */
    m_fScaleX = fX;
}

void CDisplayable::setYScale(float fY)
{
    /**
     * @param float y The y value of the transformation
     */
    m_fScaleY = fY;
}

void CDisplayable::setZScale(float fZ)
{
    /**
     * @param float z The z value of the transformation
     */
    m_fScaleZ = fZ;
}

float CDisplayable::getScaleX() const
{
    /**
     * @return The x value of the transformation
     */
    return m_fScaleX;
}

float CDisplayable::getScaleY() const
{
    /**
     * @return The y value of the transformation
     */
    return m_fScaleY;
}

float CDisplayable::getScaleZ() const
{
    /**
     * @return The z value of the transformation
     */
    return m_fScaleZ;
}

void CDisplayable::getScale(float &fX, float &fY, float &fZ)
{
    /**
     * @param float &x The x value of the transformation
     * @param float &y The y value of the transformation
     * @param float &z The z value of the transformation
     */
    fX = m_fScaleX;
    fY = m_fScaleY;
    fZ = m_fScaleZ;
}

void CDisplayable::setScale(float fX, float fY, float fZ)
{
    /**
     * Scales this given object by a specified value in each axis.
     *
     * @param float x The amount to scale in the X axis.
     * @param float y The amount to scale in the Y axis.
     * @param float z The amount to scale in the Z axis.
     */
    m_fScaleX = fX;
    m_fScaleY = fY;
    m_fScaleZ = fZ;
}

//void CDisplayable::setXVelocity(float fAmount)
//{
//    /**
//     * @param float amount The amount to apply to the X velocity.
//     */
//    m_fVelocityX = fAmount;
//}
//
//void CDisplayable::setYVelocity(float fAmount)
//{
//    /**
//     * @param float amount The amount to apply to the Y velocity.
//     */
//    m_fVelocityY = fAmount;
//}
//
//void CDisplayable::setZVelocity(float fAmount)
//{
//    /**
//     * @param float amount The amount to apply to the Z velocity.
//     */
//    m_fVelocityZ = fAmount;
//}
//
//void CDisplayable::setVelocitiy(float fX, float fY, float fZ)
//{
//    /**
//     * @param float x The amount to apply to the X velocity.
//     * @param float y The amount to apply to the Y velocity.
//     * @param float z The amount to apply to the Z velocity.
//     */
//    setXVelocity(fX);
//    setYVelocity(fY);
//    setZVelocity(fZ);
//}
//
//float CDisplayable::getXVelocitiy() const
//{
//    /**
//     * @return float The X velocity
//     */
//    return m_fVelocityX;
//}
//
//float CDisplayable::getYVelocitiy() const
//{
//    /**
//     * @return float The Y velocity
//     */
//    return m_fVelocityY;
//}
//
//float CDisplayable::getZVelocitiy() const
//{
//    /**
//     * @return float The Z velocity
//     */
//    return m_fVelocityZ;
//}
//
//void CDisplayable::getVelocity(float &fX, float &fY, float &fZ)
//{
//    /**
//     * @param float &x The X velocity
//     * @param float &x The Y velocity
//     * @param float &x The Z velocity
//     */
//    fX = getXVelocitiy();
//    fY = getXVelocitiy();
//    fZ = getXVelocitiy();
//}

bool CDisplayable::isVisible() const
{
    /**
     * @return bool Whether or not the object is visible.
     */
    return m_bVisible;
}

void CDisplayable::show()
{
    /**
     * Turns on the visibility of the object.
     */
    setVisible(true);
}

void CDisplayable::hide()
{
    /**
     * Turns off the visibility of the object.
     */
    setVisible(false);
}

void CDisplayable::setVisible(bool bVisible)
{
    /**
     * @param bool bVisible The visibility of the object to assign.
     */
    m_bVisible = bVisible;
}

bool CDisplayable::hasMaterial(DWORD nSubset) const
{
    /**
     * @param DWORD nSubset The subset to test for material validity.
     * @return bool Whether or not a mesh has a particular material for a given
     *      subset.
     */
    return m_arrMeshMaterials.find(nSubset) != m_arrMeshMaterials.end();
}

const CMaterial& CDisplayable::getMaterial(DWORD nSubset) const
{
    /**
     * @param DWORD nSubset The subset of the mesh to obtain the material.
     * @return const CMaterial& A reference to the material value for the subset
     * @throw runtime_error If the subset does not contain a material.
     */
    if (! hasMaterial(nSubset))
    {
        stringstream sstream;
        sstream << "CDisplayable::getMaterial() - "
                << "Cannot obtain material for subset "
                << nSubset
                << " for displayable object.";
        throw std::runtime_error(sstream.str());
    }
    return m_arrMeshMaterials.find(nSubset)->second;
}

bool CDisplayable::hasTexture(DWORD nSubset) const
{
    /**
     * @param DWORD nSubset The subset of the mesh to test for texture validity
     * @return bool Whether or not the subset has a texture for this mesh.
     */
    return (m_arrMeshTextures.find(nSubset) != m_arrMeshTextures.end() &&
            m_arrMeshTextures.find(nSubset)->second != nullptr);
}

CTexture *CDisplayable::getTexture(DWORD nSubset) const
{
    /**
     * @param DWORD nSubset The subset of the mesh to obtain the texture for.
     * @return CTexture* A pointer to the texture which exists at the subset.
     * @throw runtime_error If the subset does not contain a texture.
     */
    if (! hasTexture(nSubset))
    {
        stringstream sstream;
        sstream << "CDisplayable::getTexture() - "
                << "Cannot obtain texture for subset "
                << nSubset
                << " for displayable object.";
        throw std::runtime_error(sstream.str());
    }
    return m_arrMeshTextures.find(nSubset)->second;
}

void CDisplayable::setSubsetNonDrawable(DWORD nSubset)
{
    /**
     * Assigns a subset to be not drawn.
     *
     * @param DWORD nSubset The subset to not draw on this displayable.
     */
    m_arrNonDrawables.insert(nSubset);
}

bool CDisplayable::isSubsetDrawable(DWORD nSubset) const
{
    /**
     * @return bool Whether the subset is drawable.
     */
    return m_arrNonDrawables.find(nSubset) == m_arrNonDrawables.end();
}

void CDisplayable::setSubsetDrawable(DWORD nSubset)
{
    /**
     * Assigns a subset to be drawn.
     *
     * @param DWORD nSubset The subset to draw on this displayable.
     */
    set<DWORD>::iterator pPosition(m_arrNonDrawables.find(nSubset));
    if (pPosition != m_arrNonDrawables.end())
    {
        m_arrNonDrawables.erase(pPosition);
    }
}

TBoundingShape CDisplayable::getBoundingShape() const
{
    /**
     * @return const TBoundingShape& Returns the bounding shape of the
     *  displayable.
     */
    return m_BoundingShape;
}

void CDisplayable::showBoundingBox()
{
    /**
     * Enables bounding box for this displayable object. The next render,
     * if bounding boxes are enabled, then the bounding box for this shape
     * is shown.
     */
    m_bShowBoundingBox = true;
}

void CDisplayable::hideBoundingBox()
{
    /**
     * Disables bounding box for this displayable object. The next render,
     * if bounding boxes are enabled, then the bounding box for this shape
     * is not longer shown.
     */
    m_bShowBoundingBox = false;
}

void CDisplayable::setAutomaticallyRendered(bool bAutomaticallyRendered)
{
    /**
     * Changes whether this object can be automatically rendered by the
     * displayable container.
     * 
     * @param bool bAutomaticallyRendered The new state for being auto rendered
     */
    m_bAutomaticallyRendered = bAutomaticallyRendered;
}

bool CDisplayable::canBeAutomaticallyRendered() const
{
    /**
     * @return bool Whether this object is allowed to be automatically rendered
     *  by the displayable container.
     */
    return m_bAutomaticallyRendered;
}